package org.foliage.rcp.ui.dnd;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEffect;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.foliage.rcp.ui.internal.renderer.GridCellRenderer;
import org.foliage.rcp.ui.widgets.Grid;
import org.foliage.rcp.ui.widgets.GridColumn;
import org.foliage.rcp.ui.widgets.GridItem;

/**
 * This class provides default implementations to display a source image when a drag is initiated from a <code>Grid</code>.
 * 
 * <p>
 * Classes that wish to provide their own source image for a <code>Grid</code> can extend <code>DragSourceAdapter</code> class and override the <code>DragSourceAdapter.dragStart</code> method and set
 * the field <code>DragSourceEvent.image</code> with their own image.
 * </p>
 * 
 * Subclasses that override any methods of this class must call the corresponding <code>super</code> method to get the default drag under effect implementation.
 * 
 * @see DragSourceAdapter
 * @see DragSourceEvent
 * 
 * @since 3.3
 * @author mark-oliver.reiser
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class GridDragSourceEffect extends DragSourceEffect {

	Image dragSourceImage = null;

	/**
	 * 
	 * @param grid
	 */
	public GridDragSourceEffect(Grid grid) {
		super(grid);
	}

	/**
	 * This implementation of <code>dragFinished</code> disposes the image that was created in <code>GridDragSourceEffect.dragStart</code>.
	 * 
	 * Subclasses that override this method should call <code>super.dragFinished(event)</code> to dispose the image in the default implementation.
	 * 
	 * @param event the information associated with the drag finished event
	 */
	public void dragFinished(DragSourceEvent event) {
		if (dragSourceImage != null) dragSourceImage.dispose();
		dragSourceImage = null;
	}

	/**
	 * This implementation of <code>dragStart</code> will create a default image that will be used during the drag. The image should be disposed when the drag is completed in the
	 * <code>GridDragSourceEffect.dragFinished</code> method.
	 * 
	 * Subclasses that override this method should call <code>super.dragStart(event)</code> to use the image from the default implementation.
	 * 
	 * @param event the information associated with the drag start event
	 */
	public void dragStart(DragSourceEvent event) {
		event.image = getDragSourceImage(event);
	}

	Image getDragSourceImage(DragSourceEvent event) {
		if (dragSourceImage != null) dragSourceImage.dispose();
		dragSourceImage = null;
		Grid grid = (Grid) getControl();
		Display display = grid.getDisplay();
		Rectangle empty = new Rectangle(0, 0, 0, 0);

		// Collect the currently selected items.
		Point[] selection;
		if (grid.getCellSelectionEnabled()) {
			selection = grid.getCellSelection();
		} else {
			List l = new ArrayList();
			GridItem[] selItems = grid.getSelection();
			for (int i = 0; i < selItems.length; i++) {
				for (int j = 0; j < grid.getColumnCount(); j++) {
					if (grid.getColumn(j).isVisible()) {
						l.add(new Point(j, grid.indexOf(selItems[i])));
					}
				}
			}
			selection = (Point[]) l.toArray(new Point[l.size()]);
		}
		if (selection.length == 0) return null;

		Rectangle bounds = null;
		for (int i = 0; i < selection.length; i++) {
			GridItem item = grid.getItem(selection[i].y);
			Rectangle currBounds = item.getBounds(selection[i].x);

			if (empty.equals(currBounds)) {
				selection[i] = null;
			} else {
				if (bounds == null) {
					bounds = currBounds;
				} else {
					bounds = bounds.union(currBounds);
				}
			}
		}
		if (bounds == null) return null;
		if (bounds.width <= 0 || bounds.height <= 0) return null;

		dragSourceImage = new Image(display, bounds.width, bounds.height);
		GC gc = new GC(dragSourceImage);
		for (int i = 0; i < selection.length; i++) {
			if (selection[i] == null) continue;
			GridItem item = grid.getItem(selection[i].y);
			GridColumn column = grid.getColumn(selection[i].x);
			Rectangle currBounds = item.getBounds(selection[i].x);
			GridCellRenderer r = column.getCellRenderer();
			r.setBounds(currBounds.x - bounds.x, currBounds.y - bounds.y, currBounds.width, currBounds.height);
			gc.setClipping(currBounds.x - bounds.x - 1, currBounds.y - bounds.y - 1, currBounds.width + 2, currBounds.height + 2);
			r.setColumn(selection[i].x);
			r.setSelected(false);
			r.setFocus(false);
			r.setRowFocus(false);
			r.setCellFocus(false);
			r.setRowHover(false);
			r.setColumnHover(false);
			r.setCellSelected(false);
			r.setHoverDetail("");
			r.setDragging(true);
			r.paint(gc, item);
			gc.setClipping((Rectangle) null);
		}
		gc.dispose();

		return dragSourceImage;
	}
}
